home *** CD-ROM | disk | FTP | other *** search
- Subject: Whitepaper on Objective-C (very VERY long - 7 pgs.)
- Sent: 12/31/96 12:48
- Received: 12/31/96 13:55
- From: DLN, dneumann@next.com
- Reply-To: Semper.Fi, semper.fi@solutions.apple.com
- To: Subscribers to, semper.fi@solutions.apple.com
-
- Below is an RTF version of a whitepaper I wrote on the value-add of
- Objective-C. How it complements other languages. It's long, but I
- thought
- this list would appreciate it given the natural high frequency of Obj-C
- questions.
- Please don't flame. I realize its way above the list's etiquette size
- but
- I thought it worth the inconvenience trade-off.
- There are a lot of issues covered below -- not just syntax and
- feature-list but the impact on the developer due to a combination of
- features. I try to explain not just the What of Objective-C but the Why
- of
- it as well. The paper covers:
-
- - language integration
- - component features
- - code reuse
- - typing options
- - protocols, categories, posing, forwarding, and other language features
- - delegation, notification, generic coding, exceptions, and other language
- enabling framework features
- - how messaging works and other performance related features
-
-
-
-
-
- THE VALUE-ADD OF OBJECTIVE-C
-
- Executive Summary
- Objective-C, a super-set of ANSI C with object oriented extensions, is
- one
- of several implementation languages used to craft custom application and
- business logic in NeXT's OPENSTEP and WebObjects environments. NeXT also
- supports non-C-based varieties such as WebScript and (to an increasing
- degree) Java. In fact the strong kinship between Objective-C and Java
- let's
- developers wrap legacy C-based code in Objective-C classes and subclass
- these as Java classes (using standard industry JVMs) & visa versa.
- However, in addition to a strong Java interface, Objective-C has its own
- value-adds that help enrich the development environment and complement the
- other language alternatives available to the NeXT developer. Objective-C
- has been described as the "ultimate glue language" capable of wiring
- together both the highest and lowest levels of programming logic via a
- single paradigm, Objective-C method implementations. Thus a developer
- using
- NeXT's tools might integrate disparate programming resources such as
- critical C and C++ code along side NeXT's Objective-C code within the
- implementation of any method. At the same time - due to Objective-C's
- dynamic "pluggable" architecture - it is easy to interface Objective-C
- objects with high level components (such as OLE/COM objects in the Windows
- space) and create completely new high-level components.
- Apart from the integration features of Objective-C for preserving and
- leveraging investment, there are many other value-adds. Objective-C can
- enable programs to work more dynamically without sacrificing reliability.
- It can also enhance reuse through flexibility without sacrificing
- performance.
-
-
- I. Unifying Component Assembly & Building
-
- Any time you add a new method or add an instance variable to a class
- written in C++, any class that references the altered class in will
- require
- re-compilation (in most C++ implementations). Without recompilation the
- app
- will "break." The only way to prevent the fragile superclass problem is to
- exorcize use of C++'s object oriented features which defeats the more
- provocative reasons for using C++ in the first place. To address different
- aspects of the fragile superclass problem, one predominant solution has
- achieved widespread attention: "componentware" -- entire application
- programming architectures for carrying out component development. A
- problem with such approaches is that the tasks of assembly are very
- different from the tasks of development. Wouldn't it be nice if
- componentware was supported inside the language (at any granularity) such
- that component creation and assembly was implicit and routine?
- Objective-C was designed from the beginning to encourage the creation of
- shareable dynamic components or "Software ICs".
- Objective-C excels in the creation of Software ICs enabling the dynamic
- loading of custom code into running applications. In addition, thanks to
- its ability to incorporate legacy 3GL resources, Objective-C does not
- force
- developers to abandon perfectly good code that happened to be written in
- the "wrong" language. Merely being an Objective-C object means that that
- object can be dynamically manipulated, wired together with other
- components, and even become an OLE/COM object (via the NeXTORB) on Windows
- platforms. According to BYTE in an August 1996 article discussing language
- flexibility and components: "If functions can migrate both into and out of
- a component as a project evolves, that's helpful. A common language for
- components and glue makes this possible. NextStep programmers have
- exploited this synergy for years."
-
- Integrating 3GL Resources
-
- Developers may access legacy APIs in C and C++ along side those APIs from
- NeXT in Objective-C. As an example of an Objective-C method that
- incorporates C++, here is an initialization method for an Objective-C
- object. (An initialization method is similar to a C++ constructor.) The
- method sets values for some of the object's instance variables. The sample
- code was borrowed from a Calculator example supplied with NeXT's
- development tools.
-
- - init
- {
- cplus_object = new CalcEngine;
- previousAction = 0;
- return self;
- }
-
- The first line creates a C++ object and assigns it to an instance variable
- of the Objective-C object known as cplus_object. "CalcEngine" is the class
- of the C++ object. The next line simply assigns the value of 0 to a C
- integer called previousAction. The following Objective-C method
- implementation is an example of how C++ might be involved in handling a
- user's action from within an Objective-C method:
-
- - equalsKey:sender
- {
- [display setDoubleValue:cplus_object->equalsKey( [theTextField
- doubleValue] )];
- }
-
- Note that Objective-C messages follow the pattern of Smalltalk: an object
- reference followed by a method name and its arguments. The message
- statement is surrounded by brackets to denote where it begins and ends.
- Above we have an Objective-C message inside a C++ member function call
- inside another Objective-C message. The inner Objective-C message returns
- a
- C double that gets passed into the C++ member function as an argument. The
- C++ member function returns another C double which, in turn, is passed as
- an argument to the outer Objective-C message.
-
- Wiring it Together
-
- In NeXT's Interface Builder, the developer can wire together live
- (compiled) objects and enhance Interface Builder itself on the fly.
- Deployed applications can be extended with new features or patched using
- dynamic loading of new code. Any Objective-C object can be scripted in
- NeXT's scripting language derivative of Objective-C called WebScript. Via
- the NeXTORB on Windows platforms, this scripting can be extended to any
- application that supports OLE Automation such as Visual Basic and
- Microsoft
- Excel.
-
-
-
-
- II. Flexibility
-
- Most of the individual features exhibited by Objective-C are intertwined
- in
- subtle and not so subtle ways. The presence of one feature and its success
- depend on (or can be greatly enhanced by) the presence other features. To
- evaluate the merits of one feature in isolation can often be irrelevant or
- misleading. For example, Objective-C supports dynamic typing and dynamic
- binding. Neither of these features would be very worthwhile (or even
- desirable) without - among other things - a consistent, feature rich
- runtime information management system available to all objects.
-
- Reliable and Dynamic
-
- Many advocates of interpreted languages like Smalltalk have long stressed
- the advantages of dynamic binding and typing such as rapid prototyping.
- However advocates of the strongly typed languages like C++ have also
- stressed the reliability and discipline advantages offered by programs
- developed with compile-time type checking. So how can code exhibit
- dynamism
- which is so important to flexibility and extensibility and still offer
- bullet-proof reliability at runtime? For starters, Objective-C offers both
- dynamic and compile-time type checking. Developers can decide when and
- where dynamic typing makes sense and use "static" typing where
- appropriate.
- But the language designers went beyond supporting just class type
- checking
- alternatives.
-
- Protocols
- Although similar to a class definition (or class interface), an
- Objective-C Protocol does not define a class (which implies both behavior
- and data structure); rather, it defines pure behavior. Essentially a
- Protocol is nothing more than a group of related methods given a name in a
- manner analogous to how classes are named. Although Protocols aren't
- attached to the class hierarchy, they may be related to each other via
- inheritance (multiple inheritance actually) to capture behavior
- similarities. In this sense, Protocols are an IDL (analogous to the OO
- CORBA IDL) but supported at the language level. (As you might imagine,
- Protocols make distributed object programming rather natural in
- Objective-C.)
- Any number of completely different classes can "adopt" one or more
- Protocols. If a class adopts a protocol, the Objective-C compiler will
- check that it implements all the methods in the Protocol. Furthermore,
- objects can be dynamically typed in Objective-C while still being
- "behavior
- typed" by a Protocol. This allows the developer to let the actual class
- be resolved at runtime regardless of its position in the class hierarchy
- and still get all the benefits of type checking - that is, you find out at
- compile time if you are sending messages to an object that can't handle
- them. Developers do not have to hardcode expected classes in their object
- offerings just to achieve reliability.
- Different companies and/or different developers sharing a Protocol can
- exchange or mix code (in compiled form no less) with no knowledge of each
- other's class structures or internal implementation decisions - and yet
- have complete confidence their pieces will work together. Developers need
- only publish the Protocols they wish to export and the Protocols they
- expect client components to implement.
-
- Runtime Type Information Repository
- Objective-C has a rich runtime system in contrast to static languages
- that
- have virtually none. For instance, when you get a reference to a C++
- object
- and wish to tell it to do something, you usually do so using a pointer to
- list of function pointers. You don't know what type of object you have at
- runtime and can't find out. If the language didn't enforce strict type
- checking at compile time, these pointers could be pointing to almost
- anything except a valid function - and lead to frequent general protection
- faults (crashes).
- However, in Objective-C, dynamic typing and dynamic binding are routine
- and
- don't constrain design. Because of the Objective-C runtime information
- repository, Objective-C objects aren't just pointers being dereferenced.
- They are "alive" at runtime. Objective-C objects know who they are and can
- offer information about their class, their adopted Protocols, and their
- ability to respond to a given method. They have enough information to
- "introspect" themselves and if necessary have some other object handle a
- message sent their way. A new class can join the runtime and instances of
- that class can join the running object network.
- But how can objects (and the developers using them) absolutely count on
- the runtime support being there in every object? Afterall, a reliable,
- common, rich API for all objects isn't achievable if separate (multiple)
- inheritance trees are allowed since some objects would inherit from
- different roots with different APIs (or not inherit from the right root at
- all!). This requirement of common basic functionality for all objects
- implies single implementation inheritance which is enforced by
- Objective-C.
-
- Exceptions
- NeXT's OO Foundation Framework offers Objective-C developers NSException
- objects. When an exception occurs, an NSException object is created
- containing information about the exception. This object may be used to
- handle the problem. If unhandled, the NSException object gets passed up
- the
- stack of methods involved in handling the user's action. One important
- feature about the exception handling system is that it functions in a
- manner transparent to interprocess or intermachine communication. NeXT's
- Distributed Objects system will "forward" exceptions over the wire across
- address spaces allowing client objects to handle exceptions that weren't
- handled on a remote server.
-
-
- Reuse, Reuse, Reuse
-
- There is no such thing as a single form of reuse. There are actually many
- forms and mechanisms. However, in a discussion of 3GLs, reuse by
- inheritance is emphasized almost exclusively. Meanwhile authors familiar
- with 4GLs emphasize reuse by composition because inhertaince is often
- impossible within the 4GL environment. Objective-C has the facilities to
- empower reuse by both inheritance and composition (for both your own
- and/or
- 3rd party code). In addition, Objective-C complements and reinforces these
- reuse forms through several other reuse mechanisms such as Delegation
- (application of dynamism), Notification (application of dynamism),
- Categories (language feature), Posing (language feature), Forwarding
- (language feature), and better abstraction through Generic Objects (an
- outgrowth of many language features).
-
- Generic Objects
- An inspection of NeXT's OO frameworks illustrates how Objective-C
- inheritance has been used to reuse code. However it is important to note
- that although extremely sophisticated from a functionality standpoint,
- Objective-C frameworks (like NeXT's ApplicationKit) paradoxically tend to
- be far smaller (with fewer inheritance branches) and simpler than those
- implemented in other languages - especially the more static ones such as
- C++. Many Objective-C classes only inherit from the root object. Why
- should this be? The reason is that Objective-C classes tend to be highly
- "generic." That is, one class can work under many different circumstances
- just fine - multiple subclasses aren't required. In most cases, an
- Objective-C object can be pulled off the shelf and used as-is (via
- composition) without editing a line of code or subclassing it. There
- couldn't be a more dramatic demonstration of reuse than the ability of one
- class to accomplish the role of dozens.
- The next question is why should Objective-C encourage generic objects?
- The
- most basic reason is that the language has the infrastructure to
- effectively support dynamic typing and binding. Equally important is
- single
- inheritance which means every Objective-C object has a high common
- denominator of basic functionality that every other instance can
- absolutely
- count on.
- For example, creating universal collections (such as NSSet, NSArray, and
- NSDictionary) is routine in Objective-C because the objects they contain
- may be dynamically typed (or to put it another way, they are all of type
- NSObject). No matter what kind of object you put in these collections, the
- collections work. The compiler does not grow the code making up these
- collections regardless of how many different types of objects are used
- within them (as C++ compilers do with Templates). Developers don't have to
- recompile their applications (or NeXT's frameworks) each time a new object
- type is encountered that wishes to enter a collection even if that type is
- encountered at runtime.
- Objective-C's ability to abstract objects and make them generic is not
- limited to the dynamic handling of object types. It is also extended to
- the
- dynamic configuration of the methods that an object might wish to send.
- All
- Objective-C objects inherit the ability to take an encoded method as an
- argument and "perform" the method requested against itself or some other
- object. Thus a given object can be customized not only with your own
- objects (as "targets") but also with your own methods (as "actions").
- It is because of this dynamic message delivery support that developers in
- the OPENSTEP environment can take off-the-shelf GUI widgets provided by
- NeXT's frameworks (like buttons and sliders, etc.) and assemble them
- together with other objects (such as your business objects) that were
- utterly foreign to the developers of the GUI widgets. In contrast,
- virtually every 4GL and 3GL tool forces you to create what are all
- essentially special versions of the environment's GUI widgets. In C++ this
- means class proliferation (a handler in a subclass for every widget); in
- 4GLs it means code scattered in user interface screens.
-
- Delegation
- Delegation is a reuse concept that emphasizes cooperation over
- specialization. One common problem facing developers is intervening in the
- middle of some operation performed in third party code. For example, you
- may find everything about a third party method fine except that some other
- operation in your program really ought to be executed, checked, or aborted
- depending on how the third party method is proceeding.
- Usually the solution is to rewrite the third party method from scratch in
- a subclass. This may sound straightforward but it usually isn't. The most
- obvious problem is that you are now creating another class that you'll
- have
- to maintain. But what's worse is that you are inviting disaster because
- you
- don't necessarily know enough about the class in question to understand
- the
- "context" of the method you want to override in your subclass (such as the
- beginning and end conditions on a while loop).
- If you have source code, you can use that to write your method to set up
- the loop properly - but this violates encapsulation and ties your code to
- the third party's implementation. If you don't have source code, you can
- pretty much forget about being able to override the method successfully
- except by pure luck. This is one of the classic problems with
- implementation inheritance.
- Enter Delegation. A Delegate is nothing more than a dynamically bound and
- typed object used often in the implementation of Objective-C classes. The
- delegate can be any object, perhaps a central controller object or a
- business object. Third party objects can send messages to their delegate
- at
- appropriate times (such as within a while loop or right before one) giving
- the delegate the opportunity to approve, reject, customize, or simply
- become synchronized with what's happening in the third party object.
- Many OPENSTEP classes use delegation. For instance an NSWindow object may
- have a delegate assigned that gets informed whenever the window is
- dismissed or asked for approval right before the window is closed. Another
- type of delegate method is one that assigns a specific task to the
- delegate
- such as handling a particular operation in total. For example, an instance
- of the EODisplayGroup class asks its delegate to filter and sort its array
- of Enterprise Objects fetched from a database.
-
- Notification
- The role of delegates can be organized into three categories:
- observation,
- approval, and implementation. However a single delegate isn't always the
- best way to achieve observation for the sake of synchronization. What if
- many objects wish to be informed of important happenings in the third
- party
- object? While approval and implementation imply specific functionality and
- a defined role, observation does not.
- To simplify the task of observing activity in somebody else's object, NeXT
- has leveraged Objective-C's ability to dynamically assign actions and
- targets in a suite of optimized Foundation Framework classes based on the
- concept of Notification. These classes include NSNotification,
- NSNotificationCenter, and NSNotificationQueue.
- Objects that wish to let other objects know about what's happening to
- them
- merely "post" a notification to the NSNotificationCenter. It is the
- NSNotificationCenter's responsibility to inform the registered observers
- (if any) that the notification has happened. Thus neither the notifier or
- the observer need to know about each other - but they can still
- communicate.
- Notification combined with Delegation allow graphs of objects (that didn't
- know anything about each other at design time) to collaborate and
- intimately participate in each other's workings.
-
- Categories
- Categories provide a way to extend classes defined by others without
- subclassing. Instead of creating a new class, a Category provides a way to
- add methods directly to the existing class.
- To emphasize the power of Categories and how they differ from subclasses,
- consider a situation where a developer wants to augment functionality of a
- third-party framework - not only in one class but also among all the
- subclasses in a particular inheritance tree. In order to achieve this end,
- the developer could elect to subclass every single class underneath the
- abstract superclass or violate encapsulation by editing source code
- directly inside the abstract superclass. Categories offer a far more
- stream-lined solution. When a Category of methods is added to a given
- class, all subclasses immediately inherit the new functionality
- implemented
- in the Category. Another powerful bonus offered by Categories is that,
- like
- Objective-C classes, they may be dynamically loaded at runtime.
-
- Posing
- Posing is an Objective-C feature that let's developers create subclasses
- that masquerade as though they were members of a superclass. Why might
- this
- ability be useful? Consider the situation in which a third party has
- created an object (call it Master) that uses another third-party object
- (call it Slave) to get something done. You are interested in using Master
- as-is but also interested in customizing how Slave behaves.
- Instead of adding behavior (which categories excel at most), you want to
- override some behavior in the Slave so you create a subclass: MySlave.
- This
- is all fine except class Master is still using its original class, class
- Slave - not your MySlave subclass. So how can the developer achieve the
- desired customization while leaving Master untouched? In Objective-C, the
- answer is Posing. Posing let's developers add and reliably override
- methods
- in a subclass (even leverage the superclass's implementation) and yet tell
- the runtime system to use instances of your specialized subclass as though
- they were instances of any superclasses in the inheritance hierarchy. You
- merely tell your subclass to "poseAs" one of its superclasses.
- With Posing, class Master will use your subclass of Slave (MySlave)
- without modification - and do so at runtime, if necessary, without a
- recompile. As far as class Master is concerned, it is using class Slave.
- Other applications that use class Master but expect the functionality in
- class Slave (not MySlave) still can - without making the developer track
- two different versions of Master.
-
- Forwarding
- All Objective-C objects inherit the ability to automatically "forward"
- messages to other objects on the original target object's behalf. Thanks
- to
- the complete type information provided by the runtime, an object can
- actually detect if it can't respond to a message sent to it. When an
- object
- discovers it can't implement a method, it has a chance to handle the
- problem via a custom implementation of the forwardInvocation: method
- inherited from the root object.
- How can this enhance reuse? Forwarding enables two or more classes to
- collaborate as if they formed one class. However, unlike multiple
- inheritance strategies used in other languages, there is no ambiguity
- about
- which method gets invoked when both classes implement the same method
- because forwarding only occurs when the target of a message can't respond.
- Furthermore, collaboration via Forwarding does not lead to the creation of
- large, unwieldy classes (which have a tendency to be "dead-ends" in the
- inheritance hierarchy). Forwarding keeps implementations in separate,
- smaller objects (specialized in doing one area of functionality very well)
- but associates the collaborating objects in way transparent to the message
- sender.
- An important side benefit of Forwarding is that makes Distributed Object
- communication across address spaces transparent to the mesage sender.
-
-
-
- III. Performance
-
- Objective-C is fast. Faster than any 4GL or p-code interpreted language.
- Much of its speed stems from the language's C heritage. However the
- object-oriented features of the language benefit immensely from the
- utilization of an optimized message delivery system, just-in-time resource
- allocation, and sophisticated memory management scheme's available in
- NeXT's implementation.
- When a message is sent to an Objective-C object, the runtime system
- attempts to find an implementation for the method requested in the target
- object. It does this in manner that enforces the rules of inheritance but
- in a dynamically extendable way.
- The runtime system first looks in the target object's class definition for
- a method implementation. If not found, it then checks its superclass and
- then it's superclass's superclass and so on. Once the runtime system
- locates a method implementation, the method is invoked. To accelerate the
- message delivery process, the Objective-C runtime system actually caches
- the association of methods and the addresses of their implementations. If
- the method is found in the cache, an Objective-C message is only slightly
- slower than a function call. Once a program has been running for even a
- short while, almost all messages will be handled with cached methods.
- There are other aspects of performance beyond raw computational
- horsepower, namely overall throughput and memory consumption. The ability
- of Objective-C to exploit dynamic loading of code and object instances
- from
- archives, means it is routine for an Objective-C application to load
- resources "just in time" when and only if they are required. Parts of an
- application that aren't used frequently (like About Panels or Preferences
- Dialogs) don't grow the memory footprint at all if not used.
- Another way, Objective-C can help to reduce footprint is via "surrogate"
- objects. A surrogate is a lightweight class that uses the Objective-C
- Forwarding hook to stand-in for larger objects (such as images or movies).
- If the surrogate can't handle a message, it will create an instance of the
- heavy-weight class to handle the message on its behalf but only when
- needed
- (and do so in a manner transparent to the message sender).
- NeXT's Objective-C root object also provides mechanisms to address a
- problem common to all object-oriented applications: "bad locality of
- reference." Applications with bad locality of reference are susceptible to
- serious performance degradation due to excessive swapping to disk (an
- extremely expensive procedure) and "memory hopping" from method to method
- (which defeats popular CPU instruction caching strategies).
- In Objective-C, developers can use Categories to group related methods
- together in memory and thus improve locality of reference at the
- granularity of a class's internal implementation. In addition,
- Objective-C's support for separate allocation & initialzation during
- object
- creation encourages memory allocation by "zones" which improve locality of
- reference at the granularity of object graphs.
-
-
- - dave
-